#pragma once
#include "SpecialEquationSolver.h"
#include "Fourier.h"
#include "AlignedAllocator.h"

class DivisionAdjuster : public SpecialEquationSolver
{
	const Fourier& mFourier;	// a weak link
	int mCount;
	FloatVector mAngles;

	typedef std::vector<DirectX::XMVECTOR, AlignedAllocator<DirectX::XMVECTOR>> ColorVector;
	ColorVector mColors;
	ColorVector mIntegrals;
	ColorVector mF;
	ColorVector mDF;

	DirectX::XMVECTOR mTemporaryPrevIntegral;
	DirectX::XMVECTOR mTemporaryNextIntegral;
	float mTemporaryPrevAngle;
	float mTemporaryNextAngle;

	DirectX::XMVECTOR mTemporaryPrevColor;
	DirectX::XMVECTOR mTemporaryColor;
	DirectX::XMVECTOR mTemporaryIntegral;
	DirectX::XMVECTOR mTemporaryF;
	float mTemporaryQ;
	float mTemporaryQ1;


public:
	// constructor
	DivisionAdjuster(const Fourier&);

	void ResetWithAngles(int n, const float* angles);
	void ApplyDeltas();
	bool RawAdjustAllAngles();
	bool RawAdjustAnglesWithWrongSignOfSecondDerivative();
	bool RawAdjustAngle(int index);
	void OutputAngles(size_t n, float* outAngles) const;
	void OutputColors(size_t n, DirectX::XMVECTOR* outColors) const;

	void ResetAllColors();
	int GetIndexOfAnglesOverlap() const;

	// returns the number of surviving segments (second algorithm)
	int ReduceNumberOfSegments();

	bool MakeGradientAscent();
	float CalculateScore() const;

private:
	void ResetColorAtIndex(int index);
	void RotateUp();
	void RotateDown();
	void RotateOnlyAnglesUp();
	void RotateOnlyAnglesDown();
	float CalculateFValueForNewAngleBetweenIndexes(float angle, int prevIndex, int nextIndex);

	float CalculatePartialScore(float angle);
	float CalculateScoreForAngles(const FloatVector& angles) const;

	typedef unsigned int MaskType;
	struct ScoreAndMask { float score; MaskType mask; };
	typedef std::vector<ScoreAndMask> ScoreAndMaskVector;

	// the index for every element in the result vector indicates number of segments (minus two)
	void CalculateOptimalSubdivisions(
		ScoreAndMaskVector& outOptimalScoreAndMasks) const;

	void RawAdjustTowardsHigherScore(float& ioCurrentScore, float higherScore);
};

